package netTracker.network;

import netTracker.Util;

/**
 * A server that keeps track of nodes and assigns them unique IDs.
 * Modified heavily by David Cummings from the original source by Vijay Garg.
 */
public class NameServer implements Runnable{
	/** Utility class with debug flag: */
	Util util = new Util(this, true);
	
	final static String NAMESERVER_HOST = "172.23.7.102"; // 172.29.42.210 or 172.23.7.102
	final static int NAMESERVER_PORT = 5912;
	/** A constant ID to be assigned to the name server. */
	final static int NAMESERVER_ID = -1;
	/** The address of the name server.  */
	public final static Address NAMESERVER_ADDRESS = new Address(NAMESERVER_HOST, NAMESERVER_PORT, NAMESERVER_ID);

	/** An ID that indicates a problem. */
	final static int ERROR_ID = -2;

	
	/** A table of nodes on the network. */
	NameTable table;
	/** A post office for sending and receiving messages. */
	PostOffice pobox;
	
    /**
     * Creates a Name Server with an empty table of names.
     */
    public NameServer() {
    	table = new NameTable();
    	try {
			pobox = new PostOffice (NAMESERVER_ADDRESS);
		} catch (PostalException e) {
			System.err.println(e);
		}
    }
    
    /**
     * Handles messages from a client, including JOIN and LOOKUP.
     * @param msg the message to handle
     * @return the NameServer's reply
     */
    private Message handleMessage(Message msg) {
    	String tag = msg.getTag();	// Get the tag of the message.
    	if(tag != null){
	    	if (tag.equals("LOOKUP")) {							// LOOKUP a specified address:
	    		try{
	    			Address query = msg.getOtherAddr();			// Get address to query
	    			msg.setNumber(table.lookup(query));			// Send back the result of lookup
	    		}catch(NumberFormatException e){
	    			msg.setMsg("Lookup Error: Invalid ID");		// Send back error message for invalid ID
	    			msg.setNumber(ERROR_ID);							// Indicate error
	    		}
	        } else if (tag.equals("JOIN")) {	// JOIN request:
	            Address addr = msg.getSrc();		// Get the current address of the joining node
	            int newID = table.insert(addr);	// Insert the node into the name table
	            msg.getSrc().setID(newID);			// Set the unique ID of the node
	            msg.setOtherAddr(msg.getSrc());	// Set the "other address" to be the joining node's "new" address
	        } else if (tag.equals("LISTSERVERS")){			// Request to get the list of servers:
	        	msg.setAddressBook(table.listServers());	// Send back a list of servers
	        } else if (tag.equals("LISTALL")){	// Request to get the list of all entries:
	        	msg.setAddressBook(table.getAddressBook());
	        } else if (tag.equals("LISTRINGNEIGHBORS")){
	        	msg.setAddressBook(table.getNeighbors(msg.getSrc()));
	        }
    	}
    	msg.setSrc(NAMESERVER_ADDRESS); 
       return msg;	// Simply echoes message if there was no request to handle.
    }
    
    /**
     * Listens for new messages from clients and handles them as needed.
     */
	public void run(){
		while (true) {
			try {
                Message m = pobox.receive();				// Listen for a message
                Address destination = m.getSrc();
                pobox.send(handleMessage(m), destination);	// Send a reply
            } catch (InterruptedException e){
            	System.err.println("Error receiving message, thread  interrupted: "+e);
            } catch (PostalException e){
            	System.err.println("Error receiving message: "+e);
            }
		}
	}
	   
    public static void main(String[] args) {
    /*	Scanner s = new Scanner(System.in);
    	System.out.print("Enter the host address (leave blank for default): ");
    	String input = s.nextLine();
    	if (input.length() >= 3)	// Check for valid address
    		nameserverHost = input;
    	System.out.print("Enter the port (leave blank for default): ");
    	input = s.nextLine();
    	try{
    		nameserverPort = Integer.parseInt(input);
    	}catch(NumberFormatException x){
    	//	System.out.println("Using default port.");
    	}
    	nameserverAddress = new Address(nameserverHost, nameserverPort, NAMESERVER_ID); */
       new Thread(new NameServer()).start();		// Start the NameServer
       System.out.println("NameServer started.");
    }
}
